package com.thanos.api.blockchain;

import com.thanos.web3j.config.SystemConfig;
import com.thanos.web3j.protocol.Web3j;
import com.thanos.web3j.protocol.manage.Web3Manager;
import com.thanos.web3j.utils.ConfigResourceUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.List;

/**
 * BlockchainService.java description：Thanos链区块链SDK服务类
 *
 * @Author create on 2024-12-19
 */
@Service
public class BlockchainService {
    
    private static final Logger logger = LoggerFactory.getLogger(BlockchainService.class);
    
    private SystemConfig systemConfig;
    private Web3Manager web3Manager;
    
    @PostConstruct
    public void init() {
        try {
            // 初始化系统配置
            systemConfig = ConfigResourceUtil.loadSystemConfig();
            ConfigResourceUtil.loadLogConfig(systemConfig.logConfigPath());
            
            // 创建Web3Manager
            web3Manager = new Web3Manager(systemConfig);
            
            logger.info("BlockchainService initialized successfully");
            
        } catch (Exception e) {
            logger.error("Failed to initialize BlockchainService: {}", e.getMessage(), e);
            throw new RuntimeException("Service initialization failed", e);
        }
    }
    
    /**
     * 获取目前最新被异步执行的区块块高
     */
    public Long getLatestBeExecutedNum() {
        try {
            Web3j web3j = web3Manager.getHttpWeb3jRandomly();
            Long blockNumber = web3j.thanosGetLatestBeExecutedNum().send().getNumber();
            logger.info("Latest executed block number: {}", blockNumber);
            return blockNumber;
        } catch (Exception e) {
            logger.error("Failed to get latest executed block number: {}", e.getMessage(), e);
            throw new RuntimeException("Failed to get latest executed block number", e);
        }
    }
    
    /**
     * 获取目前最新被异步共识的区块块高
     */
    public Long getLatestConsensusNumber() {
        try {
            Web3j web3j = web3Manager.getHttpWeb3jRandomly();
            Long blockNumber = web3j.thanosGetLatestConsensusNumber().send().getNumber();
            logger.info("Latest consensus block number: {}", blockNumber);
            return blockNumber;
        } catch (Exception e) {
            logger.error("Failed to get latest consensus block number: {}", e.getMessage(), e);
            throw new RuntimeException("Failed to get latest consensus block number", e);
        }
    }
    
    /**
     * 获取目前最新的共识轮次
     */
    public Long getCurrentCommitRound() {
        try {
            Web3j web3j = web3Manager.getHttpWeb3jRandomly();
            Long round = web3j.thanosGetCurrentCommitRound().send().getNumber();
            logger.info("Current commit round: {}", round);
            return round;
        } catch (Exception e) {
            logger.error("Failed to get current commit round: {}", e.getMessage(), e);
            throw new RuntimeException("Failed to get current commit round", e);
        }
    }
    
    /**
     * 通过blockNumber获取区块链信息
     */
    public String getBlockByNumber(String blockNumber) {
        try {
            Web3j web3j = web3Manager.getHttpWeb3jRandomly();
            String blockInfo = web3j.thanosGetBlockByNumber(blockNumber).send().getResult();
            logger.info("Block info for number {}: {}", blockNumber, blockInfo);
            return blockInfo;
        } catch (Exception e) {
            logger.error("Failed to get block by number {}: {}", blockNumber, e.getMessage(), e);
            throw new RuntimeException("Failed to get block by number: " + blockNumber, e);
        }
    }
    
    /**
     * 通过交易hash获取交易信息，只能获取到网关处缓存中的历史交易
     */
    public String getEthTransactionByHash(String transactionHash) {
        try {
            Web3j web3j = web3Manager.getHttpWeb3jRandomly();
            String transactionInfo = web3j.thanosGetEthTransactionByHash(transactionHash).send().getResult();
            logger.info("Transaction info for hash {}: {}", transactionHash, transactionInfo);
            return transactionInfo;
        } catch (Exception e) {
            logger.error("Failed to get transaction by hash {}: {}", transactionHash, e.getMessage(), e);
            throw new RuntimeException("Failed to get transaction by hash: " + transactionHash, e);
        }
    }
    
    /**
     * 通过交易hash获取交易信息，可以获取到全量的历史交易
     */
    public String getEthTransactionByHashByChain(String transactionHash) {
        try {
            Web3j web3j = web3Manager.getHttpWeb3jRandomly();
            String transactionInfo = web3j.thanosGetEthTransactionByHashByChain(transactionHash).send().getResult();
            logger.info("Transaction info by chain for hash {}: {}", transactionHash, transactionInfo);
            return transactionInfo;
        } catch (Exception e) {
            logger.error("Failed to get transaction by hash from chain {}: {}", transactionHash, e.getMessage(), e);
            throw new RuntimeException("Failed to get transaction by hash from chain: " + transactionHash, e);
        }
    }
    
    /**
     * 通过交易hash批量获取交易信息
     */
    public List<String> getEthTransactionsByHashes(List<String> transactionHashList) {
        try {
            Web3j web3j = web3Manager.getHttpWeb3jRandomly();
            // 将List<String>转换为逗号分隔的字符串
            String hashesStr = String.join(",", transactionHashList);
            List<String> transactionInfo = web3j.thanosGetEthTransactionsByHashes(hashesStr).send().getResult();
            logger.info("Batch transaction info for hashes {}: {}", transactionHashList, transactionInfo);
            return transactionInfo;
        } catch (Exception e) {
            logger.error("Failed to get transactions by hashes {}: {}", transactionHashList, e.getMessage(), e);
            throw new RuntimeException("Failed to get transactions by hashes", e);
        }
    }
    
    /**
     * 通过全局节点事件hash获取事件信息，只能获取到网关处缓存中的历史全局节点事件
     */
    public String getGlobalNodeEventByHash(String eventHash) {
        try {
            Web3j web3j = web3Manager.getHttpWeb3jRandomly();
            String eventInfo = web3j.thanosGetGlobalNodeEventByHash(eventHash).send().getResult();
            logger.info("Global node event info for hash {}: {}", eventHash, eventInfo);
            return eventInfo;
        } catch (Exception e) {
            logger.error("Failed to get global node event by hash {}: {}", eventHash, e.getMessage(), e);
            throw new RuntimeException("Failed to get global node event by hash: " + eventHash, e);
        }
    }
    
    /**
     * 通过全局节点事件hash获取事件信息，可以获取到全量的历史全局节点事件
     */
    public String getGlobalNodeEventByHashByChain(String eventHash) {
        try {
            Web3j web3j = web3Manager.getHttpWeb3jRandomly();
            String eventInfo = web3j.thanosGetGlobalNodeEventByHashByChain(eventHash).send().getResult();
            logger.info("Global node event info by chain for hash {}: {}", eventHash, eventInfo);
            return eventInfo;
        } catch (Exception e) {
            logger.error("Failed to get global node event by hash from chain {}: {}", eventHash, e.getMessage(), e);
            throw new RuntimeException("Failed to get global node event by hash from chain: " + eventHash, e);
        }
    }
    
    /**
     * 通过全局节点事件hash获取事件回执
     */
    public String getGlobalNodeEventReceiptByHash(String eventHash) {
        try {
            Web3j web3j = web3Manager.getHttpWeb3jRandomly();
            String eventReceipt = web3j.thanosGetGlobalNodeEventReceiptByHash(eventHash).send().getResult();
            logger.info("Global node event receipt for hash {}: {}", eventHash, eventReceipt);
            return eventReceipt;
        } catch (Exception e) {
            logger.error("Failed to get global node event receipt by hash {}: {}", eventHash, e.getMessage(), e);
            throw new RuntimeException("Failed to get global node event receipt by hash: " + eventHash, e);
        }
    }
    
    /**
     * 发送交易请求到区块链立即执行，无需共识
     */
    public String ethCall(String rawData) {
        try {
            Web3j web3j = web3Manager.getHttpWeb3jRandomly();
            String receipt = web3j.thanosEthCall(rawData).send().getResult();
            logger.info("Eth call executed with raw data {}: {}", rawData, receipt);
            return receipt;
        } catch (Exception e) {
            logger.error("Failed to execute eth call with raw data {}: {}", rawData, e.getMessage(), e);
            throw new RuntimeException("Failed to execute eth call", e);
        }
    }
    
    /**
     * 发送全局节点事件请求到区块链执行，返回事件hash
     */
    public String sendGlobalNodeEvent(String rawData) {
        try {
            Web3j web3j = web3Manager.getHttpWeb3jRandomly();
            String eventHash = web3j.thanosSendGlobalNodeEvent(rawData).send().getResult();
            logger.info("Global node event sent with raw data {}: {}", rawData, eventHash);
            return eventHash;
        } catch (Exception e) {
            logger.error("Failed to send global node event with raw data {}: {}", rawData, e.getMessage(), e);
            throw new RuntimeException("Failed to send global node event", e);
        }
    }
    
    /**
     * 发送交易请求到区块链执行，返回交易hash
     */
    public String sendEthRawTransaction(String rawData) {
        try {
            Web3j web3j = web3Manager.getHttpWeb3jRandomly();
            String transactionHash = web3j.thanosSendEthRawTransaction(rawData).send().getResult();
            logger.info("Raw transaction sent with raw data {}: {}", rawData, transactionHash);
            return transactionHash;
        } catch (Exception e) {
            logger.error("Failed to send raw transaction with raw data {}: {}", rawData, e.getMessage(), e);
            throw new RuntimeException("Failed to send raw transaction", e);
        }
    }
    
    /**
     * 批量发送交易请求到区块链执行，返回交易hash列表
     */
    public List<String> sendEthRawTransactionList(List<String> rawDataList) {
        try {
            Web3j web3j = web3Manager.getHttpWeb3jRandomly();
            // 将String列表转换为byte[]列表
            List<byte[]> byteDataList = rawDataList.stream()
                .map(data -> org.spongycastle.util.encoders.Hex.decode(data.startsWith("0x") ? data.substring(2) : data))
                .collect(java.util.stream.Collectors.toList());
            List<String> transactionHashList = web3j.thanosSendEthRawTransactionList(byteDataList).send().getResult();
            logger.info("Batch raw transactions sent with raw data list {}: {}", rawDataList, transactionHashList);
            return transactionHashList;
        } catch (Exception e) {
            logger.error("Failed to send raw transaction list with raw data {}: {}", rawDataList, e.getMessage(), e);
            throw new RuntimeException("Failed to send raw transaction list", e);
        }
    }
}